
package com.lzx.iteam.contactssearch;

import android.database.Cursor;
import android.os.*;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Data;
import android.text.TextUtils;
import android.util.SparseIntArray;
import com.lzx.iteam.contactssearch.conversion.PinyinSearch;
import com.lzx.iteam.contactssearch.conversion.PinyinSearch.HighLightFormat;
import com.lzx.iteam.contactssearch.conversion.PinyinSearch.SearchResult;
import com.lzx.iteam.util.Constants;
import com.lzx.iteam.util.PhoneNumberArea;
import com.lzx.iteam.util.PreferenceUtil;
import com.lzx.iteam.util.StringUtil;
import com.lzx.iteam.CloudDialerActivity;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.TreeMap;

public class ContactsSearchEngine {
    private static final String TAG = "ContactsSearchEngine";

    private static final int SEARCH_TOKEN = 1;

    private static final int UPDATE_TOKEN = 2;
    
    private static final int SEARCH_GROUP_TOKEN = 3;

    private SearchHandler mSearchHandler;

    private final Object mLock = new Object();

    private OnQueryCompleteListener mListener;

    private SparseIntArray mVisibleContacts;

    private SparseIntArray mMatchNameContacts;

    private CloudDialerActivity mContext;

    private Messenger mService;

    private PreferenceUtil mPreferenceUtil;
    
    private PinyinSearch mPinyinSearch;

    public ContactsSearchEngine(CloudDialerActivity context, OnQueryCompleteListener listener) {
        mContext = context;
        mListener = listener;
        
        mPinyinSearch = PinyinSearch.getInstance(mContext.getApplicationContext());

        mVisibleContacts = new SparseIntArray();
        mMatchNameContacts = new SparseIntArray();

//        mService = mContext.getSearchService();
        mPreferenceUtil = PreferenceUtil.getInstance(mContext);
    }

    /**
     * Pause smart search engine
     */
    public void pause() {
        // do nothing
    }

    /**
     * Resume smart search engine
     */
    public void resume() {
        if (mService == null) {
//            mService = mContext.getSearchService();
        }
        if (mService != null) {
            sendUpdateMessage();
        } else {
        }
    }

    /**
     * Release resource
     */
    public void destroy() {

        mVisibleContacts.clear();
        mMatchNameContacts.clear();

        synchronized (mLock) {
            if (mSearchHandler != null) {
                mSearchHandler.removeMessages(UPDATE_TOKEN);
                mSearchHandler.removeMessages(SEARCH_TOKEN);
                mSearchHandler.getLooper().quit();
                mSearchHandler = null;
            }
        }
        mListener = null;
        mContext = null;
    }
    
    private void searchImpl(String groupId,String searchText, int ifShowPeopleNoPhone) {
        
        mPinyinSearch.mContactIdPosition.clear();
        LinkedHashSet<Long> contactsId = null;
        TreeMap<Integer, ArrayList<Long>> contactedIdMap = null;
        PhoneNumberArea phoneNumberArea = PhoneNumberArea.getInstance(mContext.getApplicationContext());
        /*
        int sortBy = mPreferenceUtil.get(PreferenceUtil.CONTACT_NAME_SORT, Integer.class);
        
        if (sortBy == Constants.ORDER_BY_CALL_LOG_INDEX) {
            contactedIdMap = new TreeMap<Integer, ArrayList<Long>>();
        }
        */
        if (StringUtil.isEmpty(searchText)) {
            mLastSearchText = null;
            mLastReturnNumber = -1;
            return;
        }
        char firstChar = searchText.charAt(0);
        if (firstChar == '*') {
            //replace *
            searchText = searchText.substring(1);
            if (!StringUtil.isEmpty(searchText)) {
                contactsId = mPinyinSearch.serachForFuzzy(searchText, true);
            }
            
        } else {
            if (firstChar != '+') {
                contactsId = searchContact(groupId,searchText, ifShowPeopleNoPhone, contactedIdMap);
                
            }

            LinkedHashSet<Long> contactsIdPhoneNumber = null;
            if ((searchText.length() >=3) && ((firstChar == '+') || ((firstChar <= '9') && (firstChar >= '0')))) 
            {
                ArrayList<String> searchCase = new ArrayList<String>();
                searchCase.add(searchText);
                String countryCode = phoneNumberArea.getCountryCode();
                String internationalCode = phoneNumberArea.getInternationalCode();
//                String isoCountryCode = phoneNumberArea.getISOCountryCode();
                
                char firstCharacter = searchText.charAt(0);
                StringBuilder sb = null;
                if (firstCharacter == '+') {
                    if (sb == null) {
                        sb = new StringBuilder();
                    }
                    sb.append(internationalCode);
                    sb.append(searchText.substring(1));
                } else if (searchText.indexOf(internationalCode) == 0) {
                    // 如果是当前国家冠码开头，则替换为+号后，再找一次，这样保证无论以+开头或者冠码开头，都能正确找到国际电话号码
                    if (sb == null) {
                        sb = new StringBuilder();
                    }
                    sb.setLength(0);
                    // sb.append(",+");
                    sb.append("+");
                    sb.append(searchText.substring(countryCode.length()));
                }
                if (sb != null && sb.length() > 0) {
                    searchCase.add(sb.toString());
                }
                // 在中国的情况下，还有最后一种情况，0086已经被去掉替换为0，例如010
                StringBuffer countryPhone = new StringBuffer();
                if (countryCode.equals("86") || countryCode.equals("61")) {
                    if (firstCharacter == '0') {
                        countryPhone.setLength(0);
                        countryPhone.append(internationalCode);
                        countryPhone.append(searchText.substring(1));
                    }
                }
                if (countryPhone.length() > 0) {
                    searchCase.add(countryPhone.toString());
                }
                if (PinyinSearch.mHaveNewInput) {
                    return;
                }
                contactsIdPhoneNumber = ContactsDBReader.getContactPeople(
                        mContext.getContentResolver(), searchCase, contactedIdMap);
            }
            
            if (PinyinSearch.mHaveNewInput) {
                return;
            }
            if (contactsId == null && contactsIdPhoneNumber != null) {
                contactsId = contactsIdPhoneNumber;
            } else if (contactsId != null && contactsIdPhoneNumber != null) {
                contactsId.addAll(contactsIdPhoneNumber);
            }

            // needSearch always true now
            /*if (searchText.length() > 2 && !needSearch) {
                if ((firstChar == '+') || ((firstChar <= '9') && (firstChar >= '0'))) {
                    contactsId = mPinyinSearch.searchExp(searchText, mNationCode.preCode,
                            mNationCode.isoCountryCode);
                }
            }*/
        }

        if (PinyinSearch.mHaveNewInput) {
            return;
        }
        
        LinkedHashSet<Long> contactedIdSet = null;

        if (contactedIdMap != null && contactedIdMap.size() > 0) {
            contactedIdSet = new LinkedHashSet<Long>();
            Integer[] keys=new Integer[contactedIdMap.keySet().size()]; 
            contactedIdMap.keySet().toArray(keys);  
            for (int i=keys.length-1;i>-1;i--) {
                if (PinyinSearch.mHaveNewInput) {
                    return;
                }
                int contactedNumber = keys[i];
                ArrayList<Long> contactedIdList = contactedIdMap.get(contactedNumber);
                Iterator<Long> contactedIdListIt = contactedIdList.iterator();
                while (contactedIdListIt.hasNext()) {
                    long contactId = contactedIdListIt.next();
                    contactedIdSet.add(contactId);
                }
            }
            contactedIdMap.clear();
        }

        if (mListener != null) {
            LinkedHashSet<Long> contactsIdResult = new LinkedHashSet<Long>();
            if (contactedIdSet != null) {
                contactsIdResult.addAll(contactedIdSet);
            }
            if (contactsId != null) {
                contactsIdResult.addAll(contactsId);
            }
            mListener.onQueryComplete(groupId,contactsIdResult);
        }
    }
    
    

    private void searchImpl(String searchText, int ifShowPeopleNoPhone) {
        
        mPinyinSearch.mContactIdPosition.clear();
        LinkedHashSet<Long> contactsId = null;
        TreeMap<Integer, ArrayList<Long>> contactedIdMap = null;
        PhoneNumberArea phoneNumberArea = PhoneNumberArea.getInstance(mContext.getApplicationContext());
        int sortBy = mPreferenceUtil.get(PreferenceUtil.CONTACT_NAME_SORT, Integer.class);
        
        if (sortBy == Constants.ORDER_BY_CALL_LOG_INDEX) {
            contactedIdMap = new TreeMap<Integer, ArrayList<Long>>();
        }
        if (StringUtil.isEmpty(searchText)) {
            mLastSearchText = null;
            mLastReturnNumber = -1;
            return;
        }
        char firstChar = searchText.charAt(0);
        if (firstChar == '*') {
            //replace *
            searchText = searchText.substring(1);
            if (!StringUtil.isEmpty(searchText)) {
                contactsId = mPinyinSearch.serachForFuzzy(searchText, true);
            }
            
        } else {
            if (firstChar != '+') {
                contactsId = searchContact(searchText, ifShowPeopleNoPhone, contactedIdMap);
                
            }

            LinkedHashSet<Long> contactsIdPhoneNumber = null;
            if ((searchText.length() >=3) && ((firstChar == '+') || ((firstChar <= '9') && (firstChar >= '0')))) 
            {
                ArrayList<String> searchCase = new ArrayList<String>();
                searchCase.add(searchText);
                String countryCode = phoneNumberArea.getCountryCode();
                String internationalCode = phoneNumberArea.getInternationalCode();
//                String isoCountryCode = phoneNumberArea.getISOCountryCode();
                
                char firstCharacter = searchText.charAt(0);
                StringBuilder sb = null;
                if (firstCharacter == '+') {
                    if (sb == null) {
                        sb = new StringBuilder();
                    }
                    sb.append(internationalCode);
                    sb.append(searchText.substring(1));
                } else if (searchText.indexOf(internationalCode) == 0) {
                    // 如果是当前国家冠码开头，则替换为+号后，再找一次，这样保证无论以+开头或者冠码开头，都能正确找到国际电话号码
                    if (sb == null) {
                        sb = new StringBuilder();
                    }
                    sb.setLength(0);
                    // sb.append(",+");
                    sb.append("+");
                    sb.append(searchText.substring(countryCode.length()));
                }
                if (sb != null && sb.length() > 0) {
                    searchCase.add(sb.toString());
                }
                // 在中国的情况下，还有最后一种情况，0086已经被去掉替换为0，例如010
                StringBuffer countryPhone = new StringBuffer();
                if (countryCode.equals("86") || countryCode.equals("61")) {
                    if (firstCharacter == '0') {
                        countryPhone.setLength(0);
                        countryPhone.append(internationalCode);
                        countryPhone.append(searchText.substring(1));
                    }
                }
                if (countryPhone.length() > 0) {
                    searchCase.add(countryPhone.toString());
                }
                if (PinyinSearch.mHaveNewInput) {
                    return;
                }
                contactsIdPhoneNumber = ContactsDBReader.getContactPeople(
                        mContext.getContentResolver(), searchCase, contactedIdMap);
            }
            
            if (PinyinSearch.mHaveNewInput) {
                return;
            }
            if (contactsId == null && contactsIdPhoneNumber != null) {
                contactsId = contactsIdPhoneNumber;
            } else if (contactsId != null && contactsIdPhoneNumber != null) {
                contactsId.addAll(contactsIdPhoneNumber);
            }

            // needSearch always true now
            /*if (searchText.length() > 2 && !needSearch) {
                if ((firstChar == '+') || ((firstChar <= '9') && (firstChar >= '0'))) {
                    contactsId = mPinyinSearch.searchExp(searchText, mNationCode.preCode,
                            mNationCode.isoCountryCode);
                }
            }*/
        }

        if (PinyinSearch.mHaveNewInput) {
            return;
        }
        
        LinkedHashSet<Long> contactedIdSet = null;

        if (contactedIdMap != null && contactedIdMap.size() > 0) {
            contactedIdSet = new LinkedHashSet<Long>();
            Integer[] keys=new Integer[contactedIdMap.keySet().size()]; 
            contactedIdMap.keySet().toArray(keys);  
            for (int i=keys.length-1;i>-1;i--) {
                if (PinyinSearch.mHaveNewInput) {
                    return;
                }
                int contactedNumber = keys[i];
                ArrayList<Long> contactedIdList = contactedIdMap.get(contactedNumber);
                Iterator<Long> contactedIdListIt = contactedIdList.iterator();
                while (contactedIdListIt.hasNext()) {
                    long contactId = contactedIdListIt.next();
                    contactedIdSet.add(contactId);
                }
            }
            contactedIdMap.clear();
        }

        if (mListener != null) {
            LinkedHashSet<Long> contactsIdResult = new LinkedHashSet<Long>();
            if (contactedIdSet != null) {
                contactsIdResult.addAll(contactedIdSet);
            }
            if (contactsId != null) {
                contactsIdResult.addAll(contactsId);
            }
            mListener.onQueryComplete(contactsIdResult);
        }
    }
    
    String mLastSearchText = null;
    int mLastReturnNumber;
    
    private LinkedHashSet<Long> searchContact(String groupId,String searchText, int ifShowPeopleNoPhone, 
            TreeMap<Integer, ArrayList<Long>> contactedIdMap) {
        
        LinkedHashSet<Long> contactIds = null;
        mMatchNameContacts.clear();

        if (mService == null) {
//            mService = mContext.getSearchService();
        }
        
        if (!StringUtil.isEmpty(mLastSearchText) && mLastSearchText.length() > 8 && !StringUtil.isEmpty(searchText)) {
            if (searchText.length() == (mLastSearchText.length() + 1)) {
                if (searchText.startsWith(mLastSearchText)) {
                    if (mLastReturnNumber == 0) {
                        mLastSearchText = searchText;
                        return null;
                    }
                }
            }
        }

        /*long enterTime = System.currentTimeMillis();
        LogSystem.i("search", "enter time:" + enterTime);*/
        TreeMap<Integer, TreeMap<Integer, TreeMap<Integer, TreeMap<String, ArrayList<SearchResult>>>>> contactIdList = null;

        boolean isAllWord = false;
        char fistCharacter = searchText.charAt(0);
        
        if ((fistCharacter >= 97) && (fistCharacter <= 122)) {
            isAllWord = true;
        }
        
        if (isAllWord) {
            contactIdList = mPinyinSearch.getContactIndexListByZm(groupId,searchText, ifShowPeopleNoPhone, contactedIdMap);
        } else {
            contactIdList = mPinyinSearch.getContactIndexListByT9(searchText, ifShowPeopleNoPhone, contactedIdMap);
        }
        /*long firstTime = System.currentTimeMillis();
        long firstPeriod = firstTime - enterTime;
        LogSystem.i("search", "first period:" + firstPeriod);*/
        mLastSearchText = searchText;
        mLastReturnNumber = 0;
        
        if (contactIdList == null && contactedIdMap == null) {
            return null;
        } else {
        }
        if (PinyinSearch.mHaveNewInput) {
            return null;
        }
        
        /*if (contactedIdMap != null) {
            if (contactIds == null) {
                contactIds = new LinkedHashSet<Long>();
            }
            Integer[] keys=new Integer[contactedIdMap.keySet().size()]; 
            contactedIdMap.keySet().toArray(keys);  
            for (int i=keys.length-1;i>-1;i--) {
                if (PinyinSearch.mHaveNewInput) {
                    return null;
                }
                int contactedNumber = keys[i];
                ArrayList<Long> contactedIdList = contactedIdMap.get(contactedNumber);
                Iterator<Long> contactedIdListIt = contactedIdList.iterator();
                while (contactedIdListIt.hasNext()) {
                    long contactId = contactedIdListIt.next();
                    contactIds.add(contactId);
                }
            }
            contactedIdMap.clear();
        }*/
        
        if (contactIdList != null) {
            Iterator<Integer> it = contactIdList.keySet().iterator();
            while (it.hasNext()) {
                if (PinyinSearch.mHaveNewInput) {
                    return null;
                }

                int rankLevel = it.next();
                TreeMap<Integer, TreeMap<Integer, TreeMap<String, ArrayList<SearchResult>>>> rankLevelMap = contactIdList
                        .get(rankLevel);
                if (contactIds == null) {
                    contactIds = new LinkedHashSet<Long>();
                }

                Integer[] keys = new Integer[rankLevelMap.keySet().size()];
                rankLevelMap.keySet().toArray(keys);
                for (int i = keys.length - 1; i > -1; i--) {
                    if (PinyinSearch.mHaveNewInput) {
                        return null;
                    }
                    int matchnameLength = keys[i];
                    TreeMap<Integer, TreeMap<String, ArrayList<SearchResult>>> pinyinMap = rankLevelMap
                            .get(matchnameLength);
                    Iterator<Integer> keyResultIt = pinyinMap.keySet().iterator();
                    while (keyResultIt.hasNext()) {
                        if (PinyinSearch.mHaveNewInput) {
                            return null;
                        }
                        Integer pinyin = keyResultIt.next();
                        TreeMap<String, ArrayList<SearchResult>> pinyinTreemap = pinyinMap
                                .get(pinyin);
                        Iterator<String> pinyinIt = pinyinTreemap.keySet().iterator();
                        while (pinyinIt.hasNext()) {
                            if (PinyinSearch.mHaveNewInput) {
                                return null;
                            }
                            String pinyinStr = pinyinIt.next();
                            ArrayList<SearchResult> objList = pinyinTreemap.get(pinyinStr);
                            Iterator<SearchResult> objListIt = objList.iterator();
                            while (objListIt.hasNext()) {
                                if (PinyinSearch.mHaveNewInput) {
                                    return null;
                                }
                                SearchResult obj = objListIt.next();
                                if (!mPinyinSearch.mContactIdPosition.containsKey(obj.contactId)) {
                                    HighLightFormat format = new HighLightFormat();
                                    format.rankLevel = obj.rankLevel;
                                    format.matchnameLength = obj.matchnameLength;
                                    mPinyinSearch.mContactIdPosition.put(obj.contactId, format);
                                }
                                contactIds.add(obj.contactId);
                            }
                        }
                    }

                }
            }

            /*
             * long secondTime = System.currentTimeMillis(); long secondPeriod =
             * secondTime - enterTime; LogSystem.i("search", "second period:" +
             * secondPeriod);
             */
            contactIdList.clear();
        }
        
        if (PinyinSearch.mHaveNewInput) {
            return null;
        }
        if (contactIds != null) {
            mLastReturnNumber = contactIds.size();
        }
        return contactIds;
    }
    
    

    private LinkedHashSet<Long> searchContact(String searchText, int ifShowPeopleNoPhone, 
            TreeMap<Integer, ArrayList<Long>> contactedIdMap) {
        
        LinkedHashSet<Long> contactIds = null;
        mMatchNameContacts.clear();

        if (mService == null) {
//            mService = mContext.getSearchService();
        }
        
        if (!StringUtil.isEmpty(mLastSearchText) && mLastSearchText.length() > 8 && !StringUtil.isEmpty(searchText)) {
            if (searchText.length() == (mLastSearchText.length() + 1)) {
                if (searchText.startsWith(mLastSearchText)) {
                    if (mLastReturnNumber == 0) {
                        mLastSearchText = searchText;
                        return null;
                    }
                }
            }
        }

        /*long enterTime = System.currentTimeMillis();
        LogSystem.i("search", "enter time:" + enterTime);*/
        TreeMap<Integer, TreeMap<Integer, TreeMap<Integer, TreeMap<String, ArrayList<SearchResult>>>>> contactIdList = null;

        boolean isAllWord = false;
        char fistCharacter = searchText.charAt(0);
        
        if ((fistCharacter >= 97) && (fistCharacter <= 122)) {
            isAllWord = true;
        }
        
        if (isAllWord) {
            contactIdList = mPinyinSearch.getContactIndexListByZm(searchText, ifShowPeopleNoPhone, contactedIdMap);
        } else {
            contactIdList = mPinyinSearch.getContactIndexListByT9(searchText, ifShowPeopleNoPhone, contactedIdMap);
        }
        /*long firstTime = System.currentTimeMillis();
        long firstPeriod = firstTime - enterTime;
        LogSystem.i("search", "first period:" + firstPeriod);*/
        mLastSearchText = searchText;
        mLastReturnNumber = 0;
        
        if (contactIdList == null && contactedIdMap == null) {
            return null;
        } else {
        }
        if (PinyinSearch.mHaveNewInput) {
            return null;
        }
        
        /*if (contactedIdMap != null) {
            if (contactIds == null) {
                contactIds = new LinkedHashSet<Long>();
            }
            Integer[] keys=new Integer[contactedIdMap.keySet().size()]; 
            contactedIdMap.keySet().toArray(keys);  
            for (int i=keys.length-1;i>-1;i--) {
                if (PinyinSearch.mHaveNewInput) {
                    return null;
                }
                int contactedNumber = keys[i];
                ArrayList<Long> contactedIdList = contactedIdMap.get(contactedNumber);
                Iterator<Long> contactedIdListIt = contactedIdList.iterator();
                while (contactedIdListIt.hasNext()) {
                    long contactId = contactedIdListIt.next();
                    contactIds.add(contactId);
                }
            }
            contactedIdMap.clear();
        }*/
        
        if (contactIdList != null) {
            Iterator<Integer> it = contactIdList.keySet().iterator();
            while (it.hasNext()) {
                if (PinyinSearch.mHaveNewInput) {
                    return null;
                }

                int rankLevel = it.next();
                TreeMap<Integer, TreeMap<Integer, TreeMap<String, ArrayList<SearchResult>>>> rankLevelMap = contactIdList
                        .get(rankLevel);
                if (contactIds == null) {
                    contactIds = new LinkedHashSet<Long>();
                }

                Integer[] keys = new Integer[rankLevelMap.keySet().size()];
                rankLevelMap.keySet().toArray(keys);
                for (int i = keys.length - 1; i > -1; i--) {
                    if (PinyinSearch.mHaveNewInput) {
                        return null;
                    }
                    int matchnameLength = keys[i];
                    TreeMap<Integer, TreeMap<String, ArrayList<SearchResult>>> pinyinMap = rankLevelMap
                            .get(matchnameLength);
                    Iterator<Integer> keyResultIt = pinyinMap.keySet().iterator();
                    while (keyResultIt.hasNext()) {
                        if (PinyinSearch.mHaveNewInput) {
                            return null;
                        }
                        Integer pinyin = keyResultIt.next();
                        TreeMap<String, ArrayList<SearchResult>> pinyinTreemap = pinyinMap
                                .get(pinyin);
                        Iterator<String> pinyinIt = pinyinTreemap.keySet().iterator();
                        while (pinyinIt.hasNext()) {
                            if (PinyinSearch.mHaveNewInput) {
                                return null;
                            }
                            String pinyinStr = pinyinIt.next();
                            ArrayList<SearchResult> objList = pinyinTreemap.get(pinyinStr);
                            Iterator<SearchResult> objListIt = objList.iterator();
                            while (objListIt.hasNext()) {
                                if (PinyinSearch.mHaveNewInput) {
                                    return null;
                                }
                                SearchResult obj = objListIt.next();
                                if (!mPinyinSearch.mContactIdPosition.containsKey(obj.contactId)) {
                                    HighLightFormat format = new HighLightFormat();
                                    format.rankLevel = obj.rankLevel;
                                    format.matchnameLength = obj.matchnameLength;
                                    mPinyinSearch.mContactIdPosition.put(obj.contactId, format);
                                }
                                contactIds.add(obj.contactId);
                            }
                        }
                    }

                }
            }

            /*
             * long secondTime = System.currentTimeMillis(); long secondPeriod =
             * secondTime - enterTime; LogSystem.i("search", "second period:" +
             * secondPeriod);
             */
            contactIdList.clear();
        }
        
        if (PinyinSearch.mHaveNewInput) {
            return null;
        }
        if (contactIds != null) {
            mLastReturnNumber = contactIds.size();
        }
        return contactIds;
    }
        
    /**
     * Update search scope
     */
    private void updateSearchScope() {
        int[] visibleContacts = null;

        if (mContext == null) {
            return;
        }

        StringBuilder sb = new StringBuilder();
        sb.append(Data.IN_VISIBLE_GROUP).append("=1").append(" AND ").append(Data.MIMETYPE)
                .append("='").append(Phone.CONTENT_ITEM_TYPE).append("'");

        //name_raw_contact_id
        String[] projection = new String[] {
                Data.CONTACT_ID, "name_raw_contact_id", Data.CONTACT_PRESENCE
        };

        Cursor cursor = null;
        try {
            if (mContext == null) {
                return;
            }
            mVisibleContacts.clear();
            cursor = mContext.getContentResolver().query(Data.CONTENT_URI, projection,
                    sb.toString(), null, null);
            if (cursor == null) {
                return;
            }
            while (cursor.moveToNext()) {
                int contactId = cursor.getInt(0);
                int rawContactId = cursor.getInt(1);
                if (mVisibleContacts.get(contactId, -1) == -1) {
                    mVisibleContacts.append(contactId, rawContactId);
                }
            }
            int size = mVisibleContacts.size();
            visibleContacts = new int[size];

            for (int i = 0; i < size; i++) {
                visibleContacts[i] = mVisibleContacts.valueAt(i);
            }
            /*if (mService != null) {
                mService.updateSearchScope(visibleContacts);
            }*/
            Message msg = Message.obtain(null, Constants.GLOBAL_MSG_UPDATE_SEARCHSCOPE);
            msg.obj = visibleContacts;
            mService.send(msg);
            
        } catch (Exception e) {
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * Send search message
     * 
     * @param key the String that is the key of searching
     */
    public void sendSearchMessage(String key, boolean ifShowPeopleNoPhone) {
        synchronized (mLock) {
            if (!initSearchHandler()) {
                return;
            }
            Message msg = mSearchHandler.obtainMessage(SEARCH_TOKEN);
            msg.obj = key;
            if (ifShowPeopleNoPhone) {
                msg.arg1 = 1;
            } else {
                msg.arg1 = 0;
            }

//            PinyinSearch.getInstance(mContext.getApplicationContext()).setHaveNewInput(true);
            PinyinSearch.setHaveNewInput(true);
            mSearchHandler.removeCallbacksAndMessages(null);
            mSearchHandler.sendMessage(msg);
//            mSearchHandler.sendMessageDelayed(msg, SEARCH_DELAY_MILLIS);
        }
    }
    
    
    public void sendSearchMessage(String groupId,String key, boolean ifShowPeopleNoPhone) {
        synchronized (mLock) {
            if (!initSearchHandler()) {
                return;
            }
            Message msg = mSearchHandler.obtainMessage(SEARCH_GROUP_TOKEN);
            
            Map<String,String> map = new HashMap<String,String>();
            map.put("groupId", groupId);
            map.put("key", key);
            
            msg.obj = map;
            if (ifShowPeopleNoPhone) {
                msg.arg1 = 1;
            } else {
                msg.arg1 = 0;
            }

//            PinyinSearch.getInstance(mContext.getApplicationContext()).setHaveNewInput(true);
            PinyinSearch.setHaveNewInput(true);
            mSearchHandler.removeCallbacksAndMessages(null);
            mSearchHandler.sendMessage(msg);
//            mSearchHandler.sendMessageDelayed(msg, SEARCH_DELAY_MILLIS);
        }
    }
    
    
    
    
    
    

    /**
     * Send update search scope message
     */
    private void sendUpdateMessage() {
        synchronized (mLock) {
            if (!initSearchHandler()) {
                return;
            }
            Message msg = mSearchHandler.obtainMessage(UPDATE_TOKEN);

            mSearchHandler.removeMessages(UPDATE_TOKEN);
            mSearchHandler.sendMessageAtFrontOfQueue(msg);
        }
    }

    /**
     * Send update search scope message
     */
    private boolean initSearchHandler() {
        if (mSearchHandler == null) {
            HandlerThread thread = new HandlerThread(TAG,
                    android.os.Process.THREAD_PRIORITY_BACKGROUND);
            thread.start();

            final Looper looper = thread.getLooper();
            if (looper == null) {
                // SpbLog.d(TAG, "thread.getLooper() returned null");
                return false;
            }
            mSearchHandler = new SearchHandler(looper, this);
        }

        return true;
    }

    /** The handler class that receive and handle message */
    private static class SearchHandler extends Handler {
        private WeakReference<ContactsSearchEngine> mWeakRef;

        public SearchHandler(final Looper looper, ContactsSearchEngine engine) {
            super(looper);
            mWeakRef = new WeakReference<ContactsSearchEngine>(engine);
        }

        public void handleMessage(Message msg) {
            ContactsSearchEngine engine = mWeakRef.get();
            if (engine == null) {
                return;
            }

            int what = msg.what;
            switch (what) {
                case SEARCH_TOKEN:
                {
                    String key = (String) msg.obj;
                    PinyinSearch.setHaveNewInput(false);
                    engine.searchImpl(key, msg.arg1);
                }
                    break;
                    
                case SEARCH_GROUP_TOKEN:
                {
                	Map<String,String> map = (Map<String,String>)msg.obj;
                    String key = map.get("key");
                    String groupId = map.get("groupId");
                    PinyinSearch.setHaveNewInput(false);
                    engine.searchImpl(groupId,key, msg.arg1);
                }
                    break;                    
                    
                    
                case UPDATE_TOKEN:
                {
                    engine.updateSearchScope();
                }
                    break;
            }
        }
    }

    public static class SearchKey {

        private String mKey;

        private String mNumber;

        private boolean mIsInvalid;

        private boolean mIsAllWord = false;
        
//        private boolean mIfShowPeopleNoPhone= false;

        public SearchKey(String str) {
            mKey = str;
            mIsInvalid = TextUtils.isEmpty(mKey);
            if (!mIsInvalid) {
                char fistCharacter = mKey.charAt(0);
                //a-z
                if ((fistCharacter >= 97) && (fistCharacter <= 122)) {
                    mIsAllWord = true;
                }
            }
        }

        public boolean isAllWord() {
            return mIsAllWord;
        }
        
        public boolean isInvalid() {
            return mIsInvalid;
        }

        public String getKey() {
            return mKey;
        }

        public String getNumber() {
            return mNumber;
        }

        @Override
        public String toString() {
            return mKey;
        }
    }
}
